//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2013 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_FUNCTORS_PREDICATES_CASE_H_
#define _BUOLA_FUNCTORS_PREDICATES_CASE_H_

#include <buola/functors/predicates/unary.h>
#include <buola/functors/predicates/binary.h>
#include <buola/utility/ulocale.h>
#include <cwctype>

namespace buola { namespace fn {

///\addtogroup functors
///@{

namespace detail {
    
    /////////////////////////////////////////////////////////////////////////////////////////////////
    ///
    /// base class for case-insensitive predicates
    ///
    /// Takes care of locale and facet storage and case-conversion
    ///
    /// Check \ref AIsOfType for a explanation on why the default copy-constructor
    /// is ok.
    ///
    /////////////////////////////////////////////////////////////////////////////////////////////////
    struct AICompare
    {
        explicit AICompare(const ULocale &pLocale)
            :   mLocale(pLocale)
        {}

        char ToLower(const char &pChar) const
        {
            return ::tolower(pChar);
        }

        wchar_t ToLower(const wchar_t &pChar) const
        {
            return ::towlower(pChar);
        }

    private:
        ULocale mLocale;
    };
        
/*namespace detail*/ }

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// equal binary predicate (case-insensitive)
///
/// Evaluated with two arguments \p p1 and \p p2, returns true if 
/// <tt>to_lower(p1)==to_lower(p2)</tt>
///
/////////////////////////////////////////////////////////////////////////////////////////////////
struct AIEqual : public detail::AICompare, public GBinaryPredicate
{
    explicit AIEqual(const ULocale &pLocale=ULocale())
        :   AICompare(pLocale)
    {}
    
    ///returns \c true if \p p1 is equal to \p p2 case-insensitively
    template<typename t1,typename t2>
    bool operator()(const t1 &p1,const t2 &p2) const
    {
        return ToLower(p1)==ToLower(p2);
    }
};
    
/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// equal binary predicate (case-insensitive)
///
/// Evaluated with two arguments \p p1 and \p p2, returns true if 
/// <tt>to_lower(p1)!=to_lower(p2)</tt>
///
/////////////////////////////////////////////////////////////////////////////////////////////////
struct ANotIEqual : public detail::AICompare, public GBinaryPredicate
{
    explicit ANotIEqual(const ULocale &pLocale=ULocale())
        :   AICompare(pLocale)
    {}
    
    ///returns \c true if \p p1 is not equal to \p p2 case-insensitively
    template<typename t1,typename t2>
    bool operator()(const t1 &p1,const t2 &p2) const
    {
        return ToLower(p1)!=ToLower(p2);
    }
};
    
/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// less binary predicate (case-insensitive)
///
/// Evaluated with two arguments \p p1 and \p p2, returns true if 
/// <tt>to_lower(p1)<to_lower(p2)</tt>
///
/////////////////////////////////////////////////////////////////////////////////////////////////
struct AILess : public detail::AICompare, public GBinaryPredicate
{
    explicit AILess(const ULocale &pLocale=ULocale())
        :   AICompare(pLocale)
    {}
    
    ///returns \c true if \p p1 is less than \p p2 case-insensitively
    template<typename t1,typename t2>
    bool operator()(const t1 &p1,const t2 &p2) const
    {
        return ToLower(p1)<ToLower(p2);
    }
};
    
/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// greater binary predicate (case-insensitive)
///
/// Evaluated with two arguments \p p1 and \p p2, returns true if 
/// <tt>to_lower(p1)>to_lower(p2)</tt>
///
/////////////////////////////////////////////////////////////////////////////////////////////////
struct AIGreater : public detail::AICompare, public GBinaryPredicate
{
    explicit AIGreater(const ULocale &pLocale=ULocale())
        :   AICompare(pLocale)
    {}
    
    ///returns \c true if \p p1 is greater than \p p2 case-insensitively
    template<typename t1,typename t2>
    bool operator()(const t1 &p1,const t2 &p2) const
    {
        return ToLower(p1)>ToLower(p2);
    }
};
    
/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// less-or-equal binary predicate (case-insensitive)
///
/// Evaluated with two arguments \p p1 and \p p2, returns true if 
/// <tt>to_lower(p1)<=to_lower(p2)</tt>
///
/////////////////////////////////////////////////////////////////////////////////////////////////
struct AILessEqual : public detail::AICompare, public GBinaryPredicate
{
    explicit AILessEqual(const ULocale &pLocale=ULocale())
        :   AICompare(pLocale)
    {}
    
    ///returns \c true if \p p1 is less or equal than \p p2 case-insensitively
    template<typename t1,typename t2>
    bool operator()(const t1 &p1,const t2 &p2) const
    {
        return ToLower(p1)<=ToLower(p2);
    }
};
    
/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// greater-or-equal binary predicate (case-insensitive)
///
/// Evaluated with two arguments \p p1 and \p p2, returns true if 
/// <tt>to_lower(p1)>=to_lower(p2)</tt>
///
/////////////////////////////////////////////////////////////////////////////////////////////////
struct AIGreaterEqual : public detail::AICompare, public GBinaryPredicate
{
    explicit AIGreaterEqual(const ULocale &pLocale=ULocale())
        :   AICompare(pLocale)
    {}
    
    ///returns \c true if \p p1 is greater or equal than \p p2 case-insensitively
    template<typename t1,typename t2>
    bool operator()(const t1 &p1,const t2 &p2) const
    {
        return ToLower(p1)>=ToLower(p2);
    }
};
    
/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// equal-to predicate (case-insensitive)
///
/// Evaluated with a single argument, returns true if the parameter is equal to the one given
/// in the constructor, case-insensitively
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tType>
struct AIEqualTo : public detail::AICompare, public GUnaryPredicate
{
    /// initialises the comparison constant from its argument
    template<typename tArg>
    explicit AIEqualTo(const tArg &pVal,const ULocale &pLocale=ULocale())
        :   AICompare(pLocale)
        ,   mVal(ToLower(pVal))
    {}
    
    /// returns \c true if \p pVal is equal to \a mVal, case-insensitively
    template<typename tParam>
    bool operator()(const tParam &pVal) const
    {
        return mVal==ToLower(pVal);
    }
    
private:
    tType mVal;
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// less-than predicate (case-insensitive)
///
/// Evaluated with a single argument, returns true if the parameter is less than the one given
/// in the constructor, case-insensitively
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tType>
struct AILessThan : public detail::AICompare
{
    /// initialises the comparison constant from its argument
    template<typename tArg>
    explicit AILessThan(const tArg &pVal,const ULocale &pLocale=ULocale())
        :   AICompare(pLocale)
        ,   mVal(ToLower(pVal))
    {}
    
    /// returns \c true if \p pVal is less than \a mVal, case-insensitively
    template<typename tParam>
    bool operator()(const tParam &pVal) const
    {
        return ToLower(pVal)<mVal;
    }
    
private:
    tType mVal;
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// greater-than predicate (case-insensitive)
///
/// Evaluated with a single argument, returns true if the parameter is greater than the one given
/// in the constructor, case-insensitively
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tType>
struct AIGreaterThan : public detail::AICompare
{
    /// initialises the comparison constant from its argument
    template<typename tArg>
    explicit AIGreaterThan(const tArg &pVal,const ULocale &pLocale=ULocale())
        :   AICompare(pLocale)
        ,   mVal(ToLower(pVal))
    {}
    
    /// returns \c true if \p pVal is greater than \a mVal, case-insensitively
    template<typename tParam>
    bool operator()(const tParam &pVal) const
    {
        return mVal<ToLower(pVal);
    }
    
private:
    tType mVal;
};

/// \name Comparison (binary, case-insensitive) predicates
//@{
    
/// returns a predicate which returns true if its argument are equal case-insensitively
inline AIEqual iequal()
{
    return AIEqual();
}

/// returns a predicate which returns true if its argument are different case-insensitively
inline ANotIEqual not_iequal()
{
    return ANotIEqual();
}

/// returns a predicate which returns true if its first argument is less than the second case-insensitively
inline AILess iless()
{
    return AILess();
}

/// returns a predicate which returns true if its first argument is greater than the second case-insensitively
inline AIGreater igreater()
{
    return AIGreater();
}

/// returns a predicate which returns true if its first argument is less or equal than the second case-insensitively
inline AILessEqual iless_equal()
{
    return AILessEqual();
}

/// returns a predicate which returns true if its first argument is greater or equal than the second case-insensitively
inline AIGreaterEqual igreater_equal()
{
    return AIGreaterEqual();
}

//@}

/// \name Comparison (unary, case-insensitive) predicates
//@{

/// returns a predicate which returns true if its argument is equal to \p pVal, case-insensitively
template<typename tType>
inline AIEqualTo<ODecay<tType>> iequal_to(tType &&pVal)
{
    return AIEqualTo<ODecay<tType>>(std::forward<tType>(pVal));
}

/// returns a predicate which returns true if its argument is less than \p pVal, case-insensitively
template<typename tType>
inline AILessThan<ODecay<tType>> iless_than(tType &&pVal)
{
    return AILessThan<ODecay<tType>>(std::forward<tType>(pVal));
}

/// returns a predicate which returns true if its argument is greater than \p pVal, case-insensitively
template<typename tType>
inline AIGreaterThan<ODecay<tType>> igreater_than(tType &&pVal)
{
    return AIGreaterThan<ODecay<tType>>(std::forward<tType>(pVal));
}

//@}

///@}

/*namespace fn*/ } /*namespace buola*/ }

#endif
